Subtrees for dynamic objects
Since ChromeEngine uses BSP for it's sorting traditionally the limitation is you can only have static scenes supported, however in ChromeEngine we've gone to great lengths to provide a workaround for dynamic objects. Our solution is called "BSP subtrees". And we'll walk you through how they work.
What are subtrees?#
Reinserting point primitives#
Re-inserting a point into a BSP tree is always O(log n) time complexity and easy to do. Here's how we do it:
- Point primitives are always leaf nodes of the BSP tree
- So removing a point primitive node from the tree is as simple as snipping off the leaf
- Finally to complete the reinsertion we just insert the point into the tree by performing a fast traversal specific to points, without any split checks.
Subtrees for dynamic objects contained within point primitives#
if we can sort point primitives into a bsp tree could we simply tree a models tree as point and then sort an entire object into the scene? Yes! To do this we introduce a new primitive called a container and we introduce the idea of dynamic objects. Here's what we do:
- Mark objects as being
dynamic - Generate each
dynamic objecttheir own subtree - Create a
container primitivefor the dynamic object, which is treated as point primitive except also points to a subtree. - We can now insert this contain primitive into the main tree, and when it is reached during traversal we recursively traverse it's subtree.
The big downside with Dynamic objects#
Dynamic objects work great in alot of cases, but the problem is since we tree the object as a point and don't split any primitives, there's a possibility that the objects primitives will be sorted wrong and this can cause visual bugs. We are trying to solve this but it's not easy.
Custom Block Descriptions#
These are the custom blocks in ChromeEngine which deal with dynamic objects:
handle_dynamic_objectsbsp.tree.insert_primbsp.tree.try_to_insert_in_containerbsp.tree.reinsert_updated_objectsbsp.tree.remove_leaf_nodebsp.tree.remove_prim_from_node
handle_dynamic_objects#
define bsp.handle_dynamic_objects \( \)
- Creates a root node if the tree is empty
- this only occurs if all objects are marked as dynamic
- Iterate over all the gameobjects and for each object marked as dynamic add it to the
@dynamic_objectslist and to the_dynamic_obj_queue - Whilst
_dynamic_obj_queuestill has items:- Pop off the first object
_*objoff the queue - If
_*objis not contained within the main tree (aka is a child of a dynamic object) and it's parent dynamic object has not been generated yet, add it back to the queue and go back to step 1. - Create a new vertex to denote the centre of the
containerprimitive. The position is equal to the bounding sphere of_*obj's centre coordinate plus the dynamic offset vector. - For each primitive assigned to
_*objinsert pointers to them at the end of the_*primslist._lowerand_**primare pointers to the start and end of the newly added sublist of_*prims. - Generate a BSP subtree using this sublist.
- Create a
containerprimitive using the previously created vertex, the radius of the dynamic objects bounding sphere and a pointer to the newly created subtree. Insert thiscontainerprimitive into the main tree or the subtree assigned to the dynamic object usingbsp.tree.insert_prim
- Pop off the first object
bsp.tree.insert_prim#
define bsp.tree.insert_prim \((_*prim)(old_**prim)(*root)(orientation) level (level) local_var_stack (stack:current)\)
Performs the insertion of a primitive into a tree. The steps are:
- Traverse the tree until we reach a point primitive node or a leaf node to find where the point primitive pointed to by
_*primshould be inserted - If the current node is Empty, then we reached a leaf node which is assigned to a polygon:
- insert the primitive pointer into the
_*primslist at the end - Create a new leaf node as a child of the leaf node to store our new primitive. Whether it's a left or right node depends on orientation.
- insert the primitive pointer into the
- Else:
- Try to insert the primitive into a
containerprimitive in case there is one usingbsp.tree.try_to_insert_in_container. - If our current node was previously node (which we can tell since it would've been marked as a TOMBSTONE) then insert our primitive pointer into the
_*primslist at the end - Else insert our primitive pointer in the same location as the first point primitive stored by the current node.
- Try to insert the primitive into a
bsp.tree.try_to_insert_in_container#
define _tmp = bsp.tree.try_to_insert_in_container \( *prim (*prim) old_**prim (old_**prim) orientation (orientation) level (level) \)
Iterates over the point primitives stored in a given BSP tree node (it assumes _current is set to the current node), and checks if they are containers. If there's more than one container it finds the closest container (by bounding sphere distance) to the primitive being inserted. If any container was found it inserts the primitive into it. It sets _tmp to 1 if it was successful else 0.
bsp.tree.reinsert_updated_objects#
define bsp.tree.reinsert_updated_objects
- For each updated object which is dynamic:
- remove the primitive from it's BSP tree node using
bsp.tree.remove_prim_from_node - pop it's prim pointer from
prims - insert the primitive back into the tree using
bsp.tree.insert_prim
- remove the primitive from it's BSP tree node using
bsp.tree.remove_prim_from_node#
define _**prim = bsp.remove_prim_from_node \((*node) (*prim)\)
- If
_*primis the only primitive in it's tree node then callbsp.tree.remove_leaf_nodeto snip off the leaf node (we can assume it's a leaf node as we only reinsert point primitives which are always leaves) - Else:
- Find the index of
_*primin the sublist of_*primsassigned to the tree node and then adjust the BSP tree to not include it. Note: we don't remove the primitive, from the list as we're going to add it back anyway and we don't want to waste computation.
- Find the index of
bsp.tree.remove_leaf_node#
define bsp.remove_leaf_node \((*node)\)
Removes a leaf node by removing all references to it within the bsp.tree.
- Remove edges to the node
- If the node is at the end of the
bsp.treelist simply delete the data, otherwise to preserve pointers just assign the first item of data to be TOMBSTONE to mark that it was deleted.